-module(passport_logic).
%%%
% passport_logic 是 passport application logic 缩写
%%%

-export([send_email_code/1]).
-export([do_login/3]).
-export([do_signup/5]).
-export([find_password/5]).
-export([do_login_transfer/2]).

-include_lib("imlib/include/log.hrl").
-include_lib("imlib/include/def_column.hrl").


-spec send_email_code(binary()) -> {error, list()} | {ok, any()}.
send_email_code(undefined) ->
    {error, "Email必须"};
% send_email_code(ToEmail) ->
%     {error, "Email必须"};
send_email_code(ToEmail) ->
    Now = imboy_dt:utc(millisecond),
    case verification_code_repo:find_by_id(ToEmail) of
        % 60000 = 60 * 1000 = 1分钟
        {ok, _Col, [{_, _, _, CreatedAt}]} when (Now - CreatedAt) < 60000 ->
            {ok, "一分钟内重复请求不发送Email"};
        {ok, _Col, [{ToEmail, Code, ValidityAt, _}]} when Now < ValidityAt ->
            Msg = <<"Code is ", Code/binary, " will expire in 10 minutes.">>,
            % ?LOG(Msg),
            % {ok, Msg};
            imboy_func:send_email(ToEmail, Msg);
        % {ok, _Col, []} ->
        _ ->
            VerifyCode = imboy_func:num_random(6),
            % 600000 = 600 * 1000 = 10分钟
            verification_code_repo:save(ToEmail, VerifyCode, Now + 600000, Now),
            Code2 = integer_to_binary(VerifyCode),
            Msg = <<"Code is ", Code2/binary, " will expire in 10 minutes.">>,
            % ?LOG(Msg),
            % {ok, Msg}
            imboy_func:send_email(ToEmail, Msg)
    end.


-spec do_login(binary(), binary(), binary()) -> {ok, any()} | {error, any()}.
do_login(_Type, _Email, <<>>) ->
    {error, "密码有误"};
do_login(Type, Email, Pwd) when Type == <<"email">> ->
    case imboy_func:is_email(Email) of
        true ->
            User = user_repo:find_by_email(Email, ?LOGIN_COLUMN),
            do_login_transfer(Pwd, User);
        false ->
            {error, "Email格式有误"}
    end;
do_login(Type, Mobile, Pwd) when Type == <<"mobile">> ->
    User = case imboy_func:is_mobile(Mobile) of
        true ->
            user_repo:find_by_mobile(Mobile, ?LOGIN_COLUMN);
        false ->
            user_repo:find_by_account(Mobile, ?LOGIN_COLUMN)
    end,
    do_login_transfer(Pwd, User).


-spec do_signup(Type :: binary(), EmailOrMobile :: binary(), Pwd :: binary(), Code :: binary(), PostVals :: list()) ->
          {ok, Msg :: list()} | {error, Msg :: list()} | {error, Msg :: list(), Code :: integer()}.
do_signup(<<"email">>, Email, Pwd, Code, PostVals) ->
    case imboy_func:is_email(Email) of
        true ->
            % 校验验证码
            case verify_code(Email, Code) of
                {ok, _} ->
                    do_signup_by_email(Email, Pwd, PostVals);
                {error, Msg} ->
                    {error, Msg}
            end;
        false ->
            {error, "Email格式有误"}
    end;
do_signup(<<"mobile">>, Mobile, Pwd, Code, PostVals) ->
    case imboy_func:is_mobile(Mobile) of
        true ->
            do_signup_by_mobile(Mobile, Pwd, Code, PostVals);
        false ->
            {error, "Email格式有误"}
    end;
do_signup(_Type, _Account, _Pwd, _Code, _PostVals) ->
    {error, "不支持的注册类型"}.


-spec find_password(Type :: binary(),
                    EmailOrMobile :: binary(),
                    Pwd :: binary(),
                    Code :: binary(),
                    PostVals :: list()) ->
          {ok, Msg :: list()} | {error, Msg :: list()} | {error, Msg :: list(), Code :: integer()}.
find_password(Type, Email, Pwd, Code, PostVals) when Type == <<"email">> ->
    case imboy_func:is_email(Email) of
        true ->
            % 校验验证码
            case verify_code(Email, Code) of
                {ok, _} ->
                    find_password_by_email(Email, Pwd, PostVals);
                {error, Msg} ->
                    {error, Msg}
            end;
        false ->
            {error, "Email格式有误"}
    end;
% find_password(Type, Mobile, Pwd, Code, PostVals) when Type == <<"mobile">> ->
%     case imboy_func:is_mobile(Mobile) of
%         true ->
%             find_password_by_mobile(Mobile, Pwd, Code, PostVals);
%         false ->
%             {error, "电话号码格式有误"}
%     end;
find_password(_Type, _Account, _Pwd, _Code, _PostVals) ->
    {error, "不支持的注册类型"}.


%% ===================================================================
%% Internal Function Definitions
%% ===================================================================


%% 校验验证码
-spec verify_code(binary(), binary()) -> {error, list()} | {ok, list()}.
verify_code(Id, Code) ->
    Now = imboy_dt:utc(millisecond),
    case verification_code_repo:find_by_id(Id) of
        {ok, _Col, [{_, Code, ValidityAt, _}]} when Now < ValidityAt ->
            {ok, "验证码有效"};
        _ ->
            {error, "验证码无效"}
    end.


-spec do_signup_by_email(binary(), binary(), list()) ->
          {ok, Msg :: list()} | {error, Msg :: list()} | {error, Msg :: list(), Code :: integer()}.
do_signup_by_email(Email, Pwd, PostVals) ->
    % ?LOG([do_signup_by_email, Email, Pwd, PostVals]),
    Id = imboy_db:pluck(user_repo:tablename()
        , <<"email='", Email/binary, "'">>
        , <<"id">>
        , 0),
    case Id of
        0 ->
            Password = imboy_cipher:rsa_decrypt(Pwd),
            Now = imboy_dt:utc(millisecond),
            Table = <<"user">>,
            Column = <<"(account,email,password,ref_user_id,
                reg_ip,reg_cosv,status,created_at)">>,
            Pwd2 = imboy_password:generate(Password),
            Now2 = integer_to_binary(Now),
            Status = integer_to_binary(1),
            Ip = proplists:get_value(<<"ip">>, PostVals, <<"{}">>),
            Cosv = proplists:get_value(<<"cosv">>, PostVals, <<>>),
            Uid0 = imboy_hashids:encode(0),
            RefUid = proplists:get_value(<<"ref_uid">>, PostVals, Uid0),
            RefUid2 = case bit_size(RefUid) > 5 of
                true ->
                    integer_to_binary(imboy_hashids:decode(RefUid));
                _ ->
                    <<"0">>
            end,
            % ?LOG(["RefUid2", RefUid2]),
            Account = integer_to_binary(account_server:allocate()),
            % ?LOG(["Email", Email]),
            % ?LOG(["Pwd2", Pwd2]),
            % ?LOG(["PostVals", PostVals]),
            % ?LOG(["Ip", Ip]),
            % ?LOG(["Cosv", Cosv]),
            Value = <<"('", Account/binary, "', '", Email/binary, "', '", Pwd2/binary, "', '", RefUid2/binary, "', '",
                      Ip/binary, "', '", Cosv/binary, "', '", Status/binary, "', '", Now2/binary, "')">>,
            imboy_db:insert_into(Table, Column, Value),
            % 注册成功
            {ok, #{}};
        _ ->
            {error, "Email已经被占用了"}
    end.


-spec do_signup_by_mobile(binary(), binary(), binary(), list()) ->
          {ok, list()} | {error, list()}.
do_signup_by_mobile(_Account, _Pwd, _Code, _PostVals) ->
    % Column = <<"id,account,password,mobile">>,
    {error, "暂时不支持手机号码注册"}.


-spec find_password_by_email(Email :: binary(), Pwd :: binary(), PostVals :: list()) ->
          {ok, Msg :: list()} | {error, Msg :: list()} | {error, Msg :: list(), Code :: integer()}.
find_password_by_email(Email, Pwd, _PostVals) ->
    Id = imboy_db:pluck(user_repo:tablename()
        , <<"email='", Email/binary, "'">>
        , <<"id">>
        , 0),
    case Id of
        0 ->
            Password = imboy_cipher:rsa_decrypt(Pwd),
            % Now = imboy_dt:utc(millisecond),
            Pwd2 = imboy_password:generate(Password),
            Where = <<"id=", (ec_cnv:to_binary(Id))/binary>>,
            Res = imboy_db:update(user_repo:tablename()
                , Where
                , #{<<"password">> => Pwd2}
            ),
            case Res of
                {ok, _} ->
                    {ok, #{}};
                Res ->
                    Res
            end;
        _ ->
            {error, "Email不存在或已被删除"}
    end.


-spec do_login_transfer(binary(), map()) -> {ok, map()} | {error, any()}.
do_login_transfer(Pwd, User) ->
    Pwd2 = maps:get(<<"password">>, User, <<>>),
    % 状态: -1 删除  0 禁用  1 启用
    Status = maps:get(<<"status">>, User, -2),
    case imboy_password:verify(Pwd, Pwd2) of
        {ok, _} when Status == -2 ->
            {error, "账号不存在"};
        {ok, _} when Status == -1 ->
            {error, "账号不存在或者已删除"};
        {ok, _} when Status == 0 ->
            {error, "账号被禁用"};
        {ok, _} when Status == 1 ->
            Id = maps:get(<<"id">>, User),
            {ok, #{
               <<"token">> => token_ds:encrypt_token(Id),
               <<"refreshtoken">> => token_ds:encrypt_refreshtoken(Id),
               <<"uid">> => imboy_hashids:encode(Id),
               <<"email">> => maps:get(<<"email">>, User),
               <<"nickname">> => maps:get(<<"nickname">>, User),
               <<"avatar">> => maps:get(<<"avatar">>, User),
               <<"account">> => maps:get(<<"account">>, User),
               <<"gender">> => maps:get(<<"gender">>, User),
               <<"region">> => maps:get(<<"region">>, User),
               <<"sign">> => maps:get(<<"sign">>, User),
               <<"role">> => 1
              }};
        {error, Msg} ->
            {error, Msg}
    end.

